
/**
 *	The world class manages all physics entities, dynamic simulation,
 *	and asynchronous queries. The world also contains efficient memory
 *	management facilities.
**/
public class b2World extends b2AllocationGuard {

	// mock fields (reachable through getter methods)
	private b2Body m_bodyList;
	private b2Joint m_jointList;
	private b2ContactManager m_contactManager;
	private b2Profile m_profile;

	// objects coming from the outer world(keep the
	// reference, preventing the gc from recycling)
	private b2Vec2 m_gravity;
	private b2DestructionListener m_destructionListener;
	private b2Draw m_debugDraw;

	/**
	 * Construct a world object.
	 * @param gravity the world gravity vector. It will be
	 * kept by this world object.
	**/
	public b2World(final b2Vec2 gravity) {

		address = jni_b2World(gravity.address);
		m_gravity = gravity;

		// mock fields
		m_profile = new b2Profile(jni_b2World_GetProfile(address));
		m_contactManager = new b2ContactManager(jni_b2World_GetContactManger(address));
	}

	@Override
	public void release() {
		if ( address != 0 ) {
			jni_b2World_release(address);

			// prevent release() be called on them.
			m_profile.address = 0;
			m_contatcManager.address = 0;
		}
		super.release();
	}

	/**
	 * Register a destruction listener. The listener is owned by you and must
	 * remain in scope.
	**/
	public void SetDestructionListener(b2DestructionListener listener) {
		jni_b2World_SetDestructionListener(address, listener.address);
		m_destructionListener = listener;
	}

	/**
	 * Register a contact filter to provide specific control over collision.
	 * Otherwise the default filter is used (b2_defaultFilter). The listener is
	 * owned by you and must remain in scope. 
	**/
	public void SetContactFilter(b2ContactFilter filter) {
		jni_b2World_SetContactFilter(address, filter.address);
		m_contactManager.m_contactFilter = filter;
	}

	/**
	 * Register a contact event listener. The listener is owned by you and must
	 * remain in scope.
	**/
	public void SetContactListener(b2ContactListener listener) {
		jni_b2World_SetContactListener(address, listener.address);
		m_contactManager.m_contactListener = listener;
	}

	/**
	 * Register a routine for debug drawing. The debug draw functions are called
	 * inside with b2World::DrawDebugData method. The debug draw object is owned
	 * by you and must remain in scope.
	**/
	public void SetDebugDraw(b2Draw debugDraw) {
		jni_b2World_SetDebugDraw(address, debugDraw.address);
		m_debugDraw = debugDraw;
	}

	/**
	 * Create a rigid body given a definition. No reference to the definition
	 * is retained.
	 * @warning This function is locked during callbacks.
	**/
	public b2Body CreateBody(final b2BodyDef def) {
		int bodyAddr = jni_b2World_CreateBody(address, def.address);

		b2Body body = new b2Body(bodyAddr);

		body.m_prev = null;
		body.m_next = m_bodyList;
		if ( m_bodyList != null ) {
			m_bodyList.m_prev = body;
		}
		m_bodyList = body;

		body.m_world = this;
		
		return body;
	}

	/**
	 * Destroy a rigid body given a definition. No reference to the definition
	 * is retained. This function is locked during callbacks.
	 * @warning This automatically deletes all associated shapes and joints.
	 * @warning This function is locked during callbacks.
	**/
	public void DestroyBody(b2Body body) {
		jni_b2World_DestroyBody(address, body.address);

		if ( body.m_prev != null ) {
			body.m_prev.m_next = body.m_next;
		}

		if (body.m_next != null)
		{
			body.m_next.m_prev = body.m_prev;
		}

		if (body == m_bodyList)
		{
			m_bodyList = body.m_next;
		}

		body.address = 0;
	}

	/**
	 * Create a joint to constrain bodies together. No reference to the definition
	 * is retained. This may cause the connected bodies to cease colliding.
	 * @warning This function is locked during callbacks.
	**/
	public b2Joint CreateJoint(final b2JointDef def) {
		int jointAddr = jni_b2World_CreateJoint(adrdress, def.address);
		b2Joint joint = new b2Joint(jointAddr);

		joint.m_prev = null;
		joint.m_next = m_jointList;
		if (m_jointList != null){
			m_jointList.m_prev = joint;
		}
		m_jointList = joint;

		return joint;
	}

	/**
	 * Destroy a joint. This may cause the connected bodies to begin colliding.
	 * @warning This function is locked during callbacks.
	**/
	public void DestroyJoint(b2Joint joint) {
		jni_b2World_DestroyJoint(address, joint.address);

		if (joint.m_prev != null)
		{
			joint.m_prev.m_next = joint.m_next;
		}

		if (joint.m_next != null)
		{
			joint.m_next.m_prev = joint.m_prev;
		}

		if (joint == m_jointList)
		{
			m_jointList = joint.m_next;
		}

		joint.address = 0;
	}


	/**
	 * Take a time step. This performs collision detection, integration,
	 * and constraint solution.
	 * @param timeStep the amount of time to simulate, this should not vary.
	 * @param velocityIterations for the velocity constraint solver.
	 * @param positionIterations for the position constraint solver.
	**/
	public void Step(float timeStep, int velocityIterations, int positionIterations) {
		jni_b2World_Step(address, timeStep, velocityIterations, positionIterations);
	}

	/**
	 * Manually clear the force buffer on all bodies. By default, forces are cleared automatically
	 * after each call to Step. The default behavior is modified by calling SetAutoClearForces.
	 * The purpose of this function is to support sub-stepping. Sub-stepping is often used to maintain
	 * a fixed sized time step under a variable frame-rate.
	 * When you perform sub-stepping you will disable auto clearing of forces and instead call
	 * ClearForces after all sub-steps are complete in one pass of your game loop.
	 * @see SetAutoClearForces
	**/
	public void ClearForces() {
		jni_b2World_ClearForces(address);
	}

	/**
	 * Call this to draw shapes and other debug draw data.
	**/
	public void DrawDebugData() {
		jni_b2World_DrawDebugData(address);
	}

	/**
	 * Query the world for all fixtures that potentially overlap the
	 * provided AABB.
	 * @param callback a user implemented callback class.
	 * @param aabb the query box.
	**/
	public void QueryAABB(b2QueryCallback callback, final b2AABB aabb) {
		jni_b2World_QueryAABB(address, callback.address, aabb.address);
	}

	/**
	 * Ray-cast the world for all fixtures in the path of the ray. Your callback
	 * controls whether you get the closest point, any point, or n-points.
	 * The ray-cast ignores shapes that contain the starting point.
	 * @param callback a user implemented callback class.
	 * @param point1 the ray starting point
	 * @param point2 the ray ending point
	**/
	public void RayCast(b2RayCastCallback callback, final b2Vec2 point1, final b2Vec2 point2) {
		jni_b2World_RayCast(address, callback.address, point1.address, point2.address);
	}

	/**
	 * Get the world body list. With the returned body, use b2Body::GetNext to get
	 * the next body in the world list. A NULL body indicates the end of the list.
	 * @return the head of the world body list.
	**/
	public b2Body GetBodyList() {
		return m_bodyList;
	}

	/**
	 * Get the world joint list. With the returned joint, use b2Joint::GetNext to get
	 * the next joint in the world list. A NULL joint indicates the end of the list.
	 * @return the head of the world joint list.
	**/
	public b2Joint GetJointList() {
		return m_jointList;
	}

	/**
	 * Get the world contact list. With the returned contact, use b2Contact::GetNext to get
	 * the next contact in the world list. A NULL contact indicates the end of the list.
	 * @return the head of the world contact list.
	 * @warning contacts are created and destroyed in the middle of a time step.
	 * Use b2ContactListener to avoid missing contacts.
	**/
	public b2Contact GetContactList() {
		return m_contactManager.m_contactList;
	}

	public void SetAllowSleeping(boolean flag) {
		jni_b2World_SetAllowSleeping(address, flag);
	}

	public boolean GetAllowSleeping() {
		return jni_b2World_GetAllowSleeping(address);
	}

	public void SetWarmStarting(boolean flag) {
		jni_b2World_SetWarmStarting(address, flag);
	}

	public boolean GetWarmStarting() {
		return jni_b2World_GetWarmStarting(address);
	}

	/**continuous physics. For testing.*/
	public void SetContinuousPhysics(boolean flag) {
		jni_b2World_SetContinuousPhysics(address, flag);
	}

	public boolean GetContinuousPhysics(){
		return jni_b2World_GetContinuousPhysics(address);
	}

	/**single stepped continuous physics. For testing.*/
	public void SetSubStepping(boolean flag) {
		jni_b2World_SetStubStepping(address, flag);
	}

	public boolean GetSubStepping() {
		return jni_b2World_SetStubStepping(address);
	}

	/**Get the number of broad-phase proxies.*/
	public int GetProxyCount() {
		return jni_b2World_GetProxyCount(address);
	}

	/**Get the number of bodies.*/
	public int GetBodyCount() {
		//TODO: if box2d doesn't create body automatically, no
		// longer need to call the original GetBodyCount(). We
		// can use a mBodyCount instead.
		return jni_b2World_GetBodyCount(address);
	}

	/**Get the number of joints.*/
	public int GetJointCount(){
		//TODO: if box2d doesn't create it automatically, no
		// longer need to call the original GetJointCount(). We
		// can use a mJointCount instead.
		return jni_b2World_GetJointCount(address);

	/**Get the number of contacts (each may have 0 or more contact points).*/
	public int GetContactCount() {
		//TODO: if box2d doesn't create it automatically, no
		// longer need to call the original GetContactCount(). We
		// can use a mContactCount instead.
		return jni_b2World_GetContactCount(address);
	}

	/**Get the height of the dynamic tree.*/
	public int GetTreeHeight() {
		return jni_b2World_GetTreeHeight(address);
	}

	/**Get the balance of the dynamic tree.*/
	public int GetTreeBalance() {
		return jni_b2World_GetTreeBalance(address);
	}

	/**
	 * Get the quality metric of the dynamic tree. The smaller the better.
	 * The minimum is 1.
	**/
	public float GetTreeQuality() {
		return jni_b2World_GetTreeQuality(address);
	}
	
	/**Change the global gravity vector.*/
	public void SetGravity(final b2Vec2 gravity) {
		jni_b2World_SetGravity(address, gravity.address);
		m_gravity = gravity;
	}

	/**Get the global gravity vector.*/
	public b2Vec2 GetGravity() {
		return m_gravity;
	}

	/**Is the world locked (in the middle of a time step).*/
	public boolean IsLocked() {
		return jni_b2World_IsLocked(address);
	}

	/**Set flag to control automatic clearing of forces after each time step.*/
	public void SetAutoClearForces(boolean flag) {
		jni_b2World_SetAutoClearForces(address, flag);
	}

	/**Get the flag that controls automatic clearing of forces after each time step.*/
	public boolean GetAutoClearForces() {
		return jni_b2World_GetAutoClearForces(address);
	}

	/**Get the contact manager for testing.*/
	public b2ContactManager GetContactManager() {
		return m_contactManager;
	}

	/**Get the current profile.*/
	public b2Profile GetProfile() {
		return m_profile;
	}

	/**
	 * Dump the world into the log file.
	 * @warning this should be called outside of a time step.
	**/
	public void Dump() {
		jni_b2World_Dump(address);
	}

	// native wrapper methods:
	private static native int jni_b2World(int gravity);
	private static native void jni_b2World_release(int me);
	private static native void jni_b2World_SetDestructionListener(int me, int listener);

	private static native void jni_b2World_SetDestructionListener(int me, int listener);
	private static native void jni_b2World_SetContactFilter(int me, int filter);
	private static native void jni_b2World_SetContactListener(int me, int listener);

	private static native void jni_b2World_SetDebugDraw(int me, int debugDraw);
	private static native int jni_b2World_CreateBody(int me, int def);
	private static native void jni_b2World_DestroyBody(int me, int body);

	private static native int jni_b2World_CreateJoint(int me, int def);
	private static native void jni_b2World_DestroyJoint(int me, int joint);

	private static native void jni_b2World_Step(int me, float timeStep, int velocityIterations, int positionIterations);
	private static native void jni_b2World_ClearForces(int me);
	private static native void jni_b2World_DrawDebugData(int me);

	private static native void jni_b2World_QueryAABB(int me, int callback, int aabb);
	private static native void jni_b2World_RayCast(int me, int callback, int point1, int point2);

	private static native void jni_b2World_SetAllowSleeping(int me, boolean flag);
	private static native boolean jni_b2World_GetAllowSleeping(int me);

	private static native void jni_b2World_SetWarmStarting(int me, boolean flag);
	private static native boolean jni_b2World_GetWarmStarting(int me);

	private static native void jni_b2World_SetContinuousPhysics(int me, boolean flag);
	private static native boolean jni_b2World_GetContinuousPhysics(int me);

	private static native void jni_b2World_SetStubStepping(int me, boolean flag);
	private static native boolean jni_b2World_SetStubStepping(int me);
	private static native int jni_b2World_GetProxyCount(int me);
	private static native int jni_b2World_GetBodyCount(int me);
	private static native int jni_b2World_GetJointCount(int me);
	private static native int jni_b2World_GetContactCount(int me);

	private static native int jni_b2World_GetTreeHeight(int me);
	private static native int jni_b2World_GetTreeBalance(int me);
	private static native float jni_b2World_GetTreeQuality(int me);
	private static native void jni_b2World_SetGravity(int me, int gravity);

	private static native boolean jni_b2World_IsLocked(int me);

	private static native void jni_b2World_SetAutoClearForces(int me, boolean flag);
	private static native boolean jni_b2World_GetAutoClearForces(int me);

	private static native int jni_b2World_GetProfile(int me);
	private static native int jni_b2World_GetContactManager(int me);

	private static native void jni_b2World_Dump(int me);
}
